﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using OpenLS.Drawing;
using OpenLS.Spreadsheet.Formats.Biff.Records;
using OpenLS.Spreadsheet.Internals;

namespace OpenLS.Spreadsheet.Formats.Biff.OfficeArt
{
    class OfficeArtWriteContext
    {
        private readonly BinaryWriter _writer;
        public readonly Dictionary<Comment, ObjRecord> CommentToObjectTable = new Dictionary<Comment, ObjRecord>();

        public OfficeArtWriteContext(BinaryWriter binaryWriter, BiffWriterWorkbookContext parentContext)
        {
            Contract.Requires(binaryWriter != null);
            this._writer = binaryWriter;
            this._parentContext = parentContext;
        }

        public Stream BaseStream
        {
            get { return Writer.BaseStream; }
        }

        public BinaryWriter Writer
        {
            get
            {
                Contract.Ensures(Contract.Result<BinaryWriter>() != null);
                return _writer;
            }
        }

        public BiffWriterWorkbookContext WorkbookContext
        {
            get { return this._parentContext; }
        }

        public void Write(byte[] toByteArray)
        {
            Writer.Write(toByteArray);
        }
        public void Write(byte item)
        {
            Writer.Write(item);
        }
        public void Write(sbyte item)
        {
            Writer.Write(item);
        }
        public void Write(ushort item)
        {
            Writer.Write(item);
        }
        public void Write(short item)
        {
            Writer.Write(item);
        }
        public void Write(int item)
        {
            Writer.Write(item);
        }
        public void Write(uint item)
        {
            Writer.Write(item);
        }

        public ObjRecord CreateObjRecord(Comment comment)
        {
            ObjRecord result = new ObjRecord(new FtNts());
            this.CommentToObjectTable.Add(comment, result);
            return result;
        }

        public void AddEmbeddedObjRecord(ObjRecord objRecord)
        {
            AddEmbeddedBiffRecord(objRecord);
        }
        private void AddEmbeddedBiffRecord(BiffRecord record)
        {
            _embeddedRecords.Add(new Pair<long, BiffRecord>(this.BaseStream.Position, record));
        }

        readonly List<Pair<long, BiffRecord>> _embeddedRecords = new List<Pair<long, BiffRecord>>();
        public IEnumerable<Pair<StreamSegment, BiffRecord>> GetSegments()
        {
            long currentPosition = 0;
            foreach (var pair in _embeddedRecords)
            {
                yield return new Pair<StreamSegment, BiffRecord>(new StreamSegment(currentPosition, pair.First), pair.Second);
                currentPosition = pair.First;
            }
            yield return new Pair<StreamSegment, BiffRecord>(new StreamSegment(currentPosition, this.BaseStream.Length), null);
            
        }
        public struct StreamSegment
        {
            private long _end;
            private long _start;

            public StreamSegment(long start, long end)
            {
                Contract.Requires(start <= end);
                this._start = start;
                this._end = end;
            }

            public bool IsEmpty
            {
                get { return Length == 0; }
            }

            public bool IsFirst
            {
                get { return _start == 0; }
            }
            public long Length{get
            {
                Contract.Ensures(Contract.Result<long>() >= 0);
                return _end - _start;
            }}

            public byte[] GetBytes(MemoryStream memoryStream)
            {
                Contract.Requires(!IsEmpty);
                memoryStream.Position = this._start;
                byte [] result = new byte[Length];
                memoryStream.Read(result, 0, result.Length);
                return result;
            }
        }

        public void AddEmbeddedTxoRecord(TxORecord txoRecord)
        {
            AddEmbeddedBiffRecord(txoRecord);
        }

        public void WriteOptionalChild(OfficeArtRecord record)
        {
            if (record == null)
                return;
            record.Write(this);
        }

        public void WriteOptionalChildren(params OfficeArtRecord[] records)
        {
            WriteOptionalChildren((IEnumerable<OfficeArtRecord>)records);
        }

        public void WriteOptionalChildren(IEnumerable<OfficeArtRecord> records)
        {
            foreach (OfficeArtRecord item in records)
            {
                this.WriteOptionalChild(item);
            }
        }

        private BiffWriterWorkbookContext _parentContext;
        public ushort GetFontId(Font font)
        {
            return _parentContext.GetFontId(font);
        }

        public int GetBlipId(Blip blip)
        {
            return _parentContext.GetBlipId(blip);
        }
    }
}